package com.doge.walknovel.utils

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Handler
import android.os.LocaleList
import android.os.Looper
import android.util.Log
import android.util.LruCache
import android.util.TypedValue
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import com.doge.walknovel.App
import com.doge.wnpro.BuildConfig
import com.doge.walknovel.api.workThreadPool
import com.google.gson.Gson
import io.reactivex.Observable
import java.io.Closeable
import java.lang.reflect.Type
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

val appContext: Context get() = App.context
val mainHandler by lazy { Handler(Looper.getMainLooper()) }

fun isMainThread() = Looper.getMainLooper() == Looper.myLooper()
fun runOnUI(block: () -> Unit) {
    if (Looper.getMainLooper() == Looper.myLooper()) {
        block()
    } else {
        mainHandler.post(block)
    }
}

val ioThreadPool by lazy { Executors.newFixedThreadPool(4)!! }
fun runOnIO(block: () -> Unit) {
    ioThreadPool.execute(block)
}

fun runOnWork(block: () -> Unit) {
    workThreadPool.execute(block)
}

fun delay(timestamp: Long, block: (Long) -> Unit) {
    Observable.timer(timestamp, TimeUnit.MILLISECONDS)
        .doOnNext(block)
        .subscribe()
}

fun log(msg: String?, tag: String = "novel") {
    if (msg?.isNotEmpty() == true && BuildConfig.DEBUG) Log.i(tag, msg)
}

fun logE(msg: String?, tag: String = "novel") {
    if (msg?.isNotEmpty() == true && BuildConfig.DEBUG) Log.e(tag, msg)
}

fun spWrite(key: String, value: Any?, folder: String) {
    log("spWrite folder $folder key $key value $value")
    appContext.getSharedPreferences(folder, Context.MODE_PRIVATE)
        .edit {
            when (value) {
                null -> putString(key, null)
                is String -> putString(key, value)
                is Int -> putInt(key, value)
                is Boolean -> putBoolean(key, value)
                is Long -> putLong(key, value)
                is Float -> putFloat(key, value)
                else -> throw IllegalArgumentException("not support value $value")
            }
        }
}

fun spReadInt(key: String, defaultValue: Int = 0, folder: String): Int {
    return appContext.getSharedPreferences(folder, Context.MODE_PRIVATE)
        .getInt(key, defaultValue).also {
            log("spReadInt folder $folder key $key value $it")
        }
}

fun spReadBoolean(key: String, defaultValue: Boolean = true, folder: String): Boolean {
    return appContext.getSharedPreferences(folder, Context.MODE_PRIVATE)
        .getBoolean(key, defaultValue).also {
            log("spReadBoolean folder $folder key $key value $it")
        }
}

fun spReadString(
    key: String,
    defaultValue: String?,
    folder: String
): String? {
    return appContext.getSharedPreferences(folder, Context.MODE_PRIVATE)
        .getString(key, defaultValue).also {
            log("spReadString folder $folder key $key value $it")
        }
}

val gson = Gson()

// 将json字符串转化为指定对象类型
fun <T> fromJson(json: String?, classOfT: Class<T>): T? {
    if (json == null) return null
    return try {
        gson.fromJson(json, classOfT)
    } catch (e: Throwable) {
        log("fromJson error $json")
        null
    }
}

fun <T> fromJson(json: String?, typeOfT: Type): T? {
    if (json == null) return null
    return try {
        gson.fromJson(json, typeOfT)
    } catch (e: Throwable) {
        log("fromJson error $json")
        null
    }
}

// 将对象转化为json字符串
fun toJson(src: Any?): String {
    if (src == null) return ""
    return gson.toJson(src)
}

fun closeQuietly(stream: Closeable?) {
    try {
        stream?.close()
    } catch (ignored: java.lang.Exception) {
    }
}

@SuppressLint("SimpleDateFormat")
val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val Long?.timeDesc: String
    get() {
        if (this == null) return ""
        return simpleDateFormat.format(Date(this))
    }

val language: Locale
    get() {
        return (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            LocaleList.getDefault().get(0)
        } else {
            Locale.getDefault()
        })
    }


val screenWidth by lazy { appContext.resources.displayMetrics.widthPixels }
val screenHeight by lazy { appContext.resources.displayMetrics.heightPixels }
private var sbh: Int? = null
val statusBarHeight: Int
    get() {
        val cache = sbh
        if (cache != null) return cache

        var height = 0
        val resourceId = appContext.resources.getIdentifier("status_bar_height", "dimen", "android")
        if (resourceId > 0) {
            height = appContext.resources.getDimensionPixelSize(resourceId)
        }
        return height.also { sbh = it }
    }

private val dps = LruCache<Int, Int>(100)
val Int.dp: Int
    get() {
        val result = dps[this]
        if (result != null && result >= 0) return result
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            this.toFloat(),
            appContext.resources.displayMetrics
        ).toInt().also {
            dps.put(this, it)
        }
    }

fun getDrawable(drawableRes: Int, scale: Float = 1F): Drawable? {
    val drawable = ContextCompat.getDrawable(appContext, drawableRes)
    drawable?.setBounds(
        0,
        0,
        (drawable.minimumWidth * scale).toInt(),
        (drawable.minimumHeight * scale).toInt()
    )
    return drawable
}

fun getString(id: Int): String = appContext.getString(id)

private val colors = mutableMapOf<String, Int>()
fun getColor(color: String): Int {
    val result = colors[color]
    if (result != null) return result
    return try {
        Color.parseColor(color).also { colors[color] = it }
    } catch (e: Throwable) {
        e.printStackTrace()
        Color.TRANSPARENT
    }
}

val String.md5: String
    get() = MD5Utils.getMD5String(this.trim { it <= ' ' }.uppercase(Locale.ROOT))

val Float?.percentage: String
    get() {
        if (this == null) return ""
        // https://blog.csdn.net/jlq_diligence/article/details/103955965
        val fmt = NumberFormat.getPercentInstance()
        fmt.maximumFractionDigits = 2// 最多两位百分小数，如25.23%
        return fmt.format(this)
    }

fun Int?.isNullOrZero() = this == null || this == 0
fun Float?.isNullOrZero() = this == null || this == 0f
